﻿using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MuHelper
{
    public class Measure
    {
        /// <summary>
        /// Flag that indicates is this measure base or complex
        /// </summary>
        public readonly bool Base;

        /// <summary>
        /// Measure Name
        /// </summary>
        public readonly string Name;

        /// <summary>
        /// Measure coefficient
        /// </summary>
        public readonly decimal Coefficient;

        /// <summary>
        /// list of basic measures in which consist complex measure
        /// </summary>
        internal readonly IDictionary<string, decimal> Nodes;

        /// <summary>
        /// Base measure constructor
        /// </summary>
        /// <param name="name">measure Name</param>
        public Measure(string name)
        {
            this.Base = true;
            this.Coefficient = 1;
            this.Name = name.Trim();

            this.Nodes = EmptyNodesArray;
        }

        /// <summary>
        /// Complex measure constructor
        /// </summary>
        /// <param name="name">measure Name</param>
        /// <param name="coefficient">measure coefficient</param>
        /// <param name="baseMeasures">base measures</param>
        public Measure(string name, decimal coefficient, IDictionary<string, decimal> baseMeasures)
        {
            this.Base = false;
            this.Name = name;
            this.Coefficient = coefficient;

            this.Nodes = new Dictionary<string, decimal>(baseMeasures.Count);
            foreach( var node in baseMeasures )
            {
                if( node.Key == "None" ) continue;
                if( node.Value == 0 ) continue;
                
                this.Nodes.Add(node.Key, node.Value);
            }
        }

        /// <summary>
        /// internal constructor
        /// </summary>
        /// <param name="coefficient">measure coefficient</param>
        /// <param name="baseMeasures">base measures</param>
        internal Measure(decimal coefficient, IDictionary<string, decimal> baseMeasures)
        {
            this.Base = false;
            this.Coefficient = coefficient;
            this.Name = string.Empty;
            this.Nodes = baseMeasures;
        }

        /// <summary>
        /// To string overrided method
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if( this.IsNone() )
            {
                return "1*(None^1)";
            }

            if( Base )
            {
                return string.Format("1*({0}^1)", this.Name);
            }
            else
            {
                var sb = new StringBuilder()
                    .Append(Coefficient).Append('*');
                foreach (var node in Nodes)
                {
                    sb.Append('(').Append(node.Key).Append('^').Append(node.Value).Append(')');
                }
                return sb.ToString();
            }
        }

        /// <summary>
        /// Checks if current measure is equivalent or equal None measure
        /// </summary>
        /// <returns>true if current measure is equivalent or equal None measure</returns>
        public bool IsNone()
        {
            return this.Name == "None" || (!this.Base && Coefficient == 1 && this.Nodes.Count == 0);
        }

        /// <summary>
        /// cheack if this measure equivalent to target measure
        /// </summary>
        /// <param name="target">target measure</param>
        /// <returns>return true if current measure is equvalent to taget measure</returns>
        public bool IsEquivalentTo(Measure target)
        {
            // b. They both base and their designation are equal
            if( this.Base && target.Base )
            {
                return this.Name == target.Name;
            }

            // c. They both have equal unordered set of nodes
            if( !this.Base && !target.Base )
            {
                if( this.Nodes.Count != target.Nodes.Count )
                {
                    return false;
                }

                return Nodes.All(thisNode => target.Nodes.Any(x => x.Key == thisNode.Key && x.Value == thisNode.Value));
            }

            // this measure are Base, other - Complex
            if( this.Base )
            {
                var targetNode = target.Nodes.First();
                return target.Nodes.Count == 1 && targetNode.Key == this.Name && targetNode.Value == 1;
            }

            // this measure are Complex, other - Base
            var node = this.Nodes.First();
            return this.Nodes.Count == 1 && node.Key == target.Name && node.Value == 1;
        }

        /// <summary>
        /// readonly array with 0 nodes used to initialize base measures
        /// </summary>
        private static readonly IDictionary<string, decimal> EmptyNodesArray;

        /// <summary>
        /// No-measure measure
        /// </summary>
        public static readonly Measure None;

        /// <summary>
        /// Static constructor
        /// </summary>
        static Measure()
        {
            EmptyNodesArray = new Dictionary<string, decimal>(0);
            None = new Measure("None");
        }
    }
}